#include "Game.h"

CGame::CGame(HINSTANCE hInstance, LPWSTR name, int mode, int isFullScreen, int frameRate)
{
	mD3d = NULL;
	mD3dDv = NULL;
	mBackBuffer = NULL;

	mMode = mode;
	SetScreenDimension(mode);
	mName = name;
	mIsFullScreen = isFullScreen;
	mFrameRate = frameRate;

	mHInstance = hInstance;
}

void CGame::SetScreenDimension(int mode)
{
	switch (mode)
	{
	case GAME_SCREEN_RESOLUTION_512_496_24:
		mScreenWidth = 512;
		mScreenHeight = 496;
		mDepth = 24;
		mBackBufferFormat = D3DFMT_X8R8G8B8;
		break;
	case GAME_SCREEN_RESOLUTION_640_480_24:
		mScreenWidth = 640;
		mScreenHeight = 480;
		mDepth = 24;
		mBackBufferFormat = D3DFMT_X8R8G8B8;
		break;

	case GAME_SCREEN_RESOLUTION_800_600_24:
		mScreenWidth = 800;
		mScreenHeight = 600;
		mDepth = 24;
		mBackBufferFormat = D3DFMT_X8R8G8B8;
		break;

	case GAME_SCREEN_RESOLUTION_1024_768_24:
		mScreenWidth = 1024;
		mScreenHeight = 768;
		mDepth = 24;
		mBackBufferFormat = D3DFMT_X8R8G8B8;
		break;

	default:
		break;
	}
}

void CGame::InitWindow()
{
	WNDCLASSEX wc;
	wc.cbSize = sizeof(WNDCLASSEX);

	wc.style = CS_HREDRAW | CS_VREDRAW;
	wc.hInstance = mHInstance;

	wc.lpfnWndProc = (WNDPROC)CGame::WinProc;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hIcon = NULL;
	wc.hCursor = LoadCursor(NULL, IDC_ARROW);
	wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wc.lpszMenuName = NULL;
	wc.lpszClassName = mName;
	wc.hIconSm = NULL;

	RegisterClassEx(&wc);

	DWORD style;
	if (mIsFullScreen)
		style = WS_EX_TOPMOST | WS_VISIBLE | WS_POPUP;
	else
		style = WS_OVERLAPPEDWINDOW;

	mHWnd =
		CreateWindow(
		mName,
		mName,
		style,
		CW_USEDEFAULT,
		CW_USEDEFAULT,
		mScreenWidth,
		mScreenHeight,
		NULL,
		NULL,
		mHInstance,
		NULL);

	if (!mHWnd)
	{
		Trace(L"[ERROR] Failed to created window!");
		DWORD ErrCode = GetLastError();
	}

	ShowWindow(mHWnd, SW_SHOWNORMAL);
	UpdateWindow(mHWnd);
}

void CGame::InitDirectX()
{
	mD3d = Direct3DCreate9(D3D_SDK_VERSION);
	D3DPRESENT_PARAMETERS d3dpp;

	ZeroMemory(&d3dpp, sizeof(d3dpp));

	d3dpp.Windowed = mIsFullScreen ? FALSE : TRUE;

	d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;

	d3dpp.BackBufferFormat = mBackBufferFormat;
	d3dpp.BackBufferCount = 1;
	d3dpp.BackBufferHeight = mScreenHeight;
	d3dpp.BackBufferWidth = mScreenWidth;

	mD3d->CreateDevice(
		D3DADAPTER_DEFAULT,
		D3DDEVTYPE_HAL,
		mHWnd,
		D3DCREATE_SOFTWARE_VERTEXPROCESSING,
		&d3dpp,
		&mD3dDv);

	if (mD3dDv == NULL)
	{
		return;
	}

	D3DXCreateSprite(mD3dDv, &mSpriteHandler);
	mD3dDv->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &mBackBuffer);
}

void CGame::InitKeyboard()
{
	if (!CInput::Instance->InitInput(mHInstance)) return;
	if (!CInput::Instance->InitKeyboard(mHWnd)) return;
}

void CGame::Init()
{
	InitWindow();
	InitDirectX();
	InitKeyboard();
	LoadResources();
}

#pragma region Get Method
int CGame::GetMode()
{
	return mMode; 
}

int CGame::GetScreenWidth()
{
	return mScreenWidth; 
}

int CGame::GetScreenHeight()
{ 
	return mScreenHeight; 
}

int CGame::GetDepth()
{
	return mDepth; 
}

LPDIRECT3D9 CGame::GetDirectX()
{
	return mD3d;
}

LPDIRECT3DDEVICE9 CGame::GetDevice()
{ 
	return this->mD3dDv; 
}

LPDIRECT3DSURFACE9 CGame::GetBackBuffer()
{
	return mBackBuffer;
}
#pragma endregion

void CGame::ProcessKeyBoard()
{
	CInput::Instance->Update();
	CInput::Instance->ProcessKeyboard();

	if (CInput::Instance->IsKeyDown(DIK_ESCAPE))
	{
		Trace(L"Escape key pressed!");
		PostMessage(mHWnd, WM_QUIT, 0, 0);
	}
}

void CGame::Render()
{
	if (mD3dDv->BeginScene())
	{
		mD3dDv->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, NULL);
		mSpriteHandler->Begin(D3DXSPRITE_ALPHABLEND);
		GameRender();
		mSpriteHandler->End();
		mD3dDv->EndScene();
	}
	mD3dDv->Present(NULL, NULL, NULL, NULL);
}

void CGame::GameLoop()
{
	MSG msg;
	int done = 0;
	DWORD frame_start = GetTickCount();;

	DWORD tick_per_frame = 1000 / mFrameRate;

	Trace(L">>> Main game loop has been started");

	while (!done)
	{
		if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
		{
			if (msg.message == WM_QUIT) done = 1;

			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}

		DWORD now = GetTickCount();
		mDeltaTime = now - frame_start;
		if (mDeltaTime >= tick_per_frame)
		{
			frame_start = now;
			ProcessInput();
			GameUpdate(mDeltaTime);
			Render();
		}
		ProcessKeyBoard();
	}
	Trace(L"Main game loop has ended");
}

#pragma region Virtual Method
void CGame::LoadResources() { }

void CGame::ProcessInput() { }

void CGame::GameUpdate(int deltaTime) { }

void CGame::GameRender() { }

void CGame::UnloadResources() { }
#pragma endregion

CGame::~CGame()
{
	UnloadResources();

	if (mD3dDv != NULL) mD3dDv->Release();
	if (mD3d != NULL) mD3d->Release();

	if (mSpriteHandler)
	{
		mSpriteHandler->Release();
	}
}

LRESULT CALLBACK CGame::WinProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message)
	{
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}

	return 0;
}


